package com.zzjson.design.proxy.my.custom.proxy;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * <p>****************************************************************************</p>
 * <p><b>Copyright © 2010-2018 rollBall team All Rights Reserved<b></p>
 * <ul style="margin:15px;">
 * <li>Description : 生成代理对象的代码</li>
 * <li>Version     : 1.0.0</li>
 * <li>Creation    : 2018年12月01日</li>
 * <li>@author     : zzy0_0</li>
 * </ul>
 * <p>****************************************************************************</p>
 */
public class MyProxy {
    private static String newLine = "\r\n";

    public static Object newProxyInstance(MyClassLoader myClassLoader, Class[] interfaces, MyInvocationHandler h) {
        try {
            //1.生成源代码
            String proxySrc = generateSrc(interfaces);
            //2.把生成的源代码输出到磁盘保存为。java文件
            String filePath = MyProxy.class.getResource("").getPath();

            File file = new File(filePath, "$Proxy1.java");
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(proxySrc.getBytes());
            fileOutputStream.flush();
            fileOutputStream.close();

            //3.把java编译为class
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

            Iterable<? extends JavaFileObject> compilationUnits1 =
                    fileManager.getJavaFileObjectsFromFiles(Arrays.asList(file));
            compiler.getTask(null, fileManager, null, null, null, compilationUnits1).call();
            Iterable<? extends JavaFileObject> compilationUnits2 =
                    fileManager.getJavaFileObjects(file);
            // reuse the same file manager to allow caching of jar files
            compiler.getTask(null, fileManager, null, null, null, compilationUnits2).call();

            fileManager.close();
            //加载到jvm
            Class proxyClass = myClassLoader.findClass("$Proxy1");
            Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
            return c.newInstance(h);
            //5.返回代理对象
        } catch (IOException | ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String generateSrc(Class[] interfaces) {
        Class clazz = interfaces[0];
        StringBuilder sb = new StringBuilder();
        sb.append("package cn.zzjson.design.proxy.my.custom.proxy; " + newLine);
        sb.append("import java.lang.reflect.Method;");
        sb.append("public class $Proxy1 implements " + clazz.getName() + "{" + newLine);
        sb.append("private MyInvocationHandler h;" + newLine);
        sb.append("public $Proxy1(MyInvocationHandler h){" + newLine);
        sb.append("this.h=h;}" + newLine);
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            sb.append("public final " + method.getReturnType() + " " + method.getName() + " (){" + newLine);
            sb.append("try{" + newLine);
            sb.append("Method m = " + clazz.getName() + ".class.getMethod(\"" + method.getName() + "\");" + newLine);
            sb.append("h.invoke(this, m,null);" + newLine);
            sb.append("}");
            sb.append("catch(Throwable t){t.printStackTrace();}}" + newLine);
        }
        sb.append("}");
        return sb.toString();
    }
}